package YAPserver.gameObjects;

import YAPenvironment.YAPconstant;
import YAPserver.logic.Game;
import YAPserver.collisionEngine.*;
/**
 * This abstract class defines all gameObjects that are circular. That means they have a radius and a speed.
 * @author Daniel
 *
 */
public abstract class AbstractBall extends AbstractGameObject implements Ball{
	
	protected final static int fieldLength = YAPconstant.C_GAMESIZE;
	
	protected boolean m_onField;
	protected Point m_nextMiddlePoint;
	protected double m_radius;
	protected boolean m_areaIntersected;
	
	/**
	 * Constructs a ballAbstract with position, direction, speed and size.
	 * @param middlePoint point which determines the position at which the ballAbstract will be constructed
	 * @param angle double which determines the direction of the ballAbstract
	 * @param speed double which determines the ballAbstract's speed
	 * @param radius double which determines the ballAbstract's radius
	 */
	AbstractBall(Point middlePoint, double angle, double speed, double radius) {
		m_radius = radius;
		//now to normalise the speed; *30 because this was the original fps
		speed = 30.*speed/Game.m_fps;
		m_line = new Line(speed, angle, middlePoint);
		m_middlePoint = m_line.getStartPoint();
//		m_line.setAngle(angle);
//		m_line.setLength(speed);
		m_nextMiddlePoint = new Point(m_line.getEndPoint());
		m_onField = true;
	}

	/**
	 * Checks whether this ballAbstract has collided with another gameObject. This includes checking for intersections with the circle area of the ballAbstract.
	 * @param object gameObject to test whether there was a collision
	 * @return <code>true</code> if there was a collision and otherwise <code>false</code>
	 */
	public boolean hasCollided(AbstractGameObject object){
		if(m_line.hasIntersection(object.getLine())){
			return true;
		}
		if(object instanceof AbstractWall){
			if(hasCollidedTrajectory(object)){
				Point point = new Point(((AbstractWall) object).getNormal());
				point.scalarMult(-this.m_radius);
				Line line = new Line(this.m_middlePoint, this.m_middlePoint.add(point));
				if(line.hasIntersection(object.getLine())){
					return true;
				}
				double distance = this.m_nextMiddlePoint.distance(object.getLine().getEndPoint());
				if(distance <= this.m_radius){
					return true;
				}
				distance = this.m_nextMiddlePoint.distance(object.getLine().getStartPoint());
				if(distance <= this.m_radius){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Only checks for intersections of the circle of this ballAbstract.
	 * @param object
	 * @return <code>true</code> if there was an intersection and otherwise <code>false</code>
	 */
	public boolean hasCollidedTrajectory(AbstractGameObject object){
		double distance = this.m_nextMiddlePoint.distance(object.getLine());
		if(distance <= this.m_radius){
			m_areaIntersected = true;
			return true;
		}
		return false;
	}
	//TODO: test and write neater
	/**
	 * @deprecated
	 * This method is no longer needed as the collision engine has been implemented differently.
	 */
	public boolean isInField(GameWall wall){
		double angleWall = Math.abs(wall.getLine().getAngle());
//		Line ballWall = new Line(wall.getLine().getStartPoint(), m_line.getEndPoint());
		Line ballWall = new Line(wall.getLine().getStartPoint(), this.m_nextMiddlePoint);
		double ballAngle = ballWall.getAngle();
		double angle;
		if(angleWall>Math.PI){
			angleWall= 2*Math.PI-angleWall;
			angle = ballAngle + angleWall;
		}else{
			angle = ballAngle - angleWall;
		}
		if(angle > 2*Math.PI){
			angle = angle-2*Math.PI;
		}
		if(angle < 0){
			angle = angle+2*Math.PI;
		}
		
		if(angle > Math.PI){
			return false;
		}
		return true;
	}
	
	public void move(){
		setMiddlePoint(new Point(this.m_nextMiddlePoint));
	}
	
	public void setSpeed(double speed){
		//automatically sets the speed according to the starting point which is the middle point
		//now to normalise the speed; *30 because this was the original fps
		speed = 30*speed/Game.m_fps;
		m_line.setLength(speed);
	}
	
	//needs testing
	
	public void setAngle(double angle){
		//sets the angle according to the starting point which is the middlePoint
		m_line.setAngle(angle);
	}
	
	//the middle point is used to determine the ball's position
	@Override
	public void setMiddlePoint(Point point){
		m_line.setStartPoint(point);
		m_middlePoint = m_line.getStartPoint();
		m_nextMiddlePoint = new Point (this.m_line.getEndPoint());
	}
	
	public void setNextMiddlePoint(Point point){
		m_nextMiddlePoint = point;
	}
	
	public double getRadius(){
		return this.m_radius;
	}
	
	public double getSpeed(){
		//unnormalise the speed
		return Game.m_fps*this.m_line.getLength()/30.;
	}
	
	public void setRadius(double radius){
		this.m_radius = radius;
	}
	
	/**
	 * Returns whether or not this ballAbstract's circle has been intersected.
	 * @return <code>true</code> if there is an intersection and <code>false</code> if not
	 */
	public boolean hasAreaIntersection(){
		return this.m_areaIntersected;
	}
	
	/**
	 * Sets whether there was an intersection or not.
	 * @param intersected boolean 
	 */
	public void setAreaIntersection(boolean intersected){
		m_areaIntersected = intersected;
	}

	
	public Point getNextMiddlePoint() {
		return this.m_nextMiddlePoint;
	}
	
	public boolean isOnField(){
		return this.m_onField;
	}
	
	public void setOnField(boolean onField){
		this.m_onField = onField;
	}
	
	@Override
	public void bounce(Wall wall){
		((AbstractGameObject) wall).getBounceBehaviour().bounce(this, wall);
	}
	
	@Override
	public double getAngle(){
		return this.m_line.getAngle();
	}
}
